home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / ps / ps.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-09-03  |  31.8 KB  |  1,249 lines

  1. /* 
  2.  * ps.c --
  3.  *
  4.  *    This file contains a program that will print out process
  5.  *    status information for one or more processes.  See the
  6.  *    man page for details on what it does.
  7.  *
  8.  * Copyright 1988 Regents of the University of California
  9.  * Permission to use, copy, modify, and distribute this
  10.  * software and its documentation for any purpose and without
  11.  * fee is hereby granted, provided that the above copyright
  12.  * notice appear in all copies.  The University of California
  13.  * makes no representations about the suitability of this
  14.  * software for any purpose.  It is provided "as is" without
  15.  * express or implied warranty.
  16.  */
  17.  
  18. #ifndef lint
  19. static char rcsid[] = "$Header: /sprite/src/cmds/ps/RCS/ps.c,v 1.22 90/09/02 20:50:50 douglis Exp $ SPRITE (Berkeley)";
  20. #endif not lint
  21.  
  22. #include <ctype.h>
  23. #include <hash.h>
  24. #include <host.h>
  25. #include <option.h>
  26. #include <proc.h>
  27. #include <pwd.h>
  28. #include <spriteTime.h>
  29. #include <status.h>
  30. #include <stdio.h>
  31. #include <stdlib.h>
  32. #include <string.h>
  33. #include <sys/ioctl.h>
  34. #include <vm.h>
  35.  
  36. /*
  37.  * Process status information may be printed in any of several ways,
  38.  * selected by command-line switches.  For each way there is a procedure
  39.  * that is called to print out in that format.  The following table
  40.  * identifies all such procedures:
  41.  */
  42.  
  43. extern void UpdateMigInfo();
  44. extern void PrintIDs(), PrintLong(), PrintMigration();
  45. extern void PrintShort(), PrintSignals(), PrintVM();
  46.  
  47. void (*(printProc[]))() = {
  48.     PrintShort,
  49.     PrintLong,
  50.     PrintIDs,
  51.     PrintVM,
  52.     PrintMigration,
  53.     PrintSignals,
  54. };
  55.  
  56. /*
  57.  * Indexes into printProc:
  58.  */
  59.  
  60. #define SHORT    0
  61. #define    LONG    1
  62. #define IDS    2
  63. #define VM    3
  64. #define MIG    4
  65. #define SIGS    5
  66.  
  67. int printIndex = 0;
  68.  
  69. /*
  70.  * Corresponding to each of the printing styles above, there is a
  71.  * corresponding sort procedure that is used as an argument to qsort
  72.  * in order to sort the process table entries in a particular way.
  73.  * 0 means don't sort:  just print them in table order.
  74.  */
  75.  
  76. extern int AgeSort(), UsageSort();
  77.  
  78. int (*(sortProc[]))() = {
  79.     AgeSort,
  80.     UsageSort,
  81.     0,
  82.     0,
  83.     0,
  84.     0
  85. };
  86.  
  87. /*
  88.  * Flags set by command-line options:
  89.  */
  90.  
  91. int aFlag =        0;    /* Non-zero means consider processes for
  92.                  * all users. */
  93. int AFlag =        0;    /* Non-zero means even consider dead procs. */
  94. int dFlag =        0;    /* Non-zero means only print info for
  95.                  * processes in debug state. */
  96. int kFlag =        0;    /* Non-zero means print out kernel processes
  97.                  * too. */
  98. int mFlag =        0;    /* Non-zero means only print out info for
  99.                  * processes in migrated state, or foreign
  100.                  * processes. */
  101. int lFlag =        0;    /* Non-zero means only print out local info for
  102.                  * migrated processes. */
  103. int lineWidth =        80;    /* Number of chars in each printed line. */
  104.  
  105. /*
  106.  * The table below describes the various command-line options that
  107.  * are understood by this program:
  108.  */
  109.  
  110. Option optionArray[] = {
  111.     OPT_DOC,        (char *) NULL,    (char *) NULL,
  112.         "This program prints out process status information.\n Synopsis:  \"ps [switches] [pid pid ...]\"\n Command-line switches are:",
  113.     OPT_TRUE,        "a",        (char *) &aFlag,
  114.         "Print info for all users' processes\n\t\tDefault: only current user's processes",
  115.     OPT_TRUE,        "A",        (char *) &AFlag,
  116.         "Print info for absolutely all user processes, even dead ones",
  117.     OPT_TRUE,        "d",        (char *) &dFlag,
  118.         "Print out only processes in DEBUG state",
  119.     OPT_CONSTANT(IDS),    "i",        (char *) &printIndex,
  120.         "Print out various process ids",
  121.     OPT_TRUE,        "k",        (char *) &kFlag,
  122.         "Print out kernel server processes as well as user processes",
  123.     OPT_TRUE,        "l",        (char *) &lFlag,
  124.         "Print out only local information for migrated processes",
  125.     OPT_TRUE,        "m",        (char *) &mFlag,
  126.         "Print out only processes that are foreign or migrated",
  127.     OPT_CONSTANT(SIGS),    "s",        (char *) &printIndex,
  128.         "Print out information about signals",
  129.     OPT_CONSTANT(LONG),    "u",        (char *) &printIndex,
  130.         "Print info in longer \"user-oriented\" form",
  131.     OPT_CONSTANT(VM),    "v",        (char *) &printIndex,
  132.         "Print virtual memory information",
  133.     OPT_INT,        "w",        (char *) &lineWidth,
  134.         "Next argument holds line width for output",
  135.     OPT_CONSTANT(MIG),    "M",        (char *) &printIndex,
  136.         "Print out migration information for migrated processes",
  137. };
  138.  
  139. /*
  140.  * The following type is used to associate a process control block an
  141.  * its argument string, so that during various sorts the two can be kept
  142.  * properly associated.
  143.  */
  144.  
  145. typedef struct {
  146.     Proc_PCBInfo *infoPtr;
  147.     Proc_PCBArgString *argString;
  148. } ControlBlock;
  149.  
  150. /*
  151.  * Miscellaneous global variables:
  152.  */
  153.  
  154. int lastPCB;        /* Set to non-zero before processing last
  155.              * control block (allows print procs to print
  156.              * totals, if they want). */
  157.  
  158. int pageSizeInKiloBytes; /* The size of the system's page in kilobytes. */
  159.  
  160. int     timerTicksPerSecond; 
  161.  
  162. /*
  163.  *----------------------------------------------------------------------
  164.  *
  165.  * main --
  166.  *
  167.  *    The main program for ps.
  168.  *
  169.  * Results:
  170.  *    None.
  171.  *
  172.  * Side effects:
  173.  *    Prints information on standard output.
  174.  *
  175.  *----------------------------------------------------------------------
  176.  */
  177.  
  178. main(argc, argv)
  179.     int argc;        /* Number of command-line arguments. */
  180.     char **argv;    /* Values of command-line arguments. */
  181. {
  182.     int i, pcbsUsed;
  183.     ReturnStatus status;
  184.     struct winsize winsize;
  185.  
  186.     pageSizeInKiloBytes = getpagesize()/1024;
  187.     timerTicksPerSecond = getTicksPerSecond();
  188.  
  189.     /*
  190.      * Find out how big the lines are, for formatting output, then
  191.      * parse options.
  192.      */
  193.  
  194.     if ((ioctl(fileno(stdout), TIOCGWINSZ, (char *) &winsize) == 0)
  195.         && (winsize.ws_col != 0)) {
  196.     lineWidth = winsize.ws_col;
  197.     } else {
  198.     char buf[1024];
  199.     char *termEnv;
  200.  
  201.     termEnv = getenv("TERM");
  202.     if (termEnv == 0) {
  203.         termEnv = "";
  204.     }
  205.     if (tgetent(buf, termEnv) == 1) {
  206.         i = tgetnum("co");
  207.         if (i > 0) {
  208.         lineWidth = i;
  209.         }
  210.     }
  211.     }
  212.  
  213.     argc = Opt_Parse(argc, argv, optionArray, Opt_Number(optionArray),
  214.         OPT_ALLOW_CLUSTERING);
  215.  
  216.     /*
  217.      * If particular process ids were given, then only print them.
  218.      * Otherwise look at all the processes in the system.
  219.      */
  220.  
  221.     if (argc > 1) {
  222.     for (i = 1; i < argc; i++) {
  223.         int pid;
  224.         Proc_PCBInfo info;
  225.         Proc_PCBInfo migInfo;
  226.         Proc_PCBArgString argString;
  227.         char *endPtr;
  228.  
  229.         pid = strtoul(argv[i], &endPtr, 16);
  230.         if (endPtr == argv[i]) {
  231.         fprintf(stderr, "Bad process id \"%s\";  ignoring.\n", argv[i]);
  232.         continue;
  233.         }
  234.         status = Proc_GetPCBInfo(Proc_PIDToIndex(pid),
  235.                      Proc_PIDToIndex(pid), PROC_MY_HOSTID,
  236.                      sizeof(info),
  237.                      &info, &argString, &pcbsUsed);
  238.         if (status != SUCCESS) {
  239.         fprintf(stderr, "Couldn't find pid \"%s\": %s.\n", argv[i],
  240.             Stat_GetMsg(status));
  241.         fflush(stderr);
  242.         continue;
  243.         }
  244.         /*
  245.          * The process we got info for may not be the one that was
  246.          * requested (different generation numbers);  check to be sure.
  247.          */
  248.  
  249.         if (pid != info.processID) {
  250.         fprintf(stderr, "Pid %s not found.\n", argv[i]);
  251.         continue;
  252.         }
  253.         if (printIndex != MIG && info.state == PROC_MIGRATED && !lFlag) {
  254.         status = Proc_GetPCBInfo(Proc_PIDToIndex(info.peerProcessID),
  255.                      Proc_PIDToIndex(info.peerProcessID),
  256.                      info.peerHostID,
  257.                      sizeof(migInfo), &migInfo, 
  258.                      (Proc_PCBArgString *) NULL,
  259.                      (int *) NULL);
  260.         if (status != SUCCESS || migInfo.state == PROC_UNUSED) {
  261.             /*
  262.              * Skip an entry that's migrated locally and doesn't
  263.              * exist remotely -- a race condition between the time
  264.              * we found out about the process and the time the process
  265.              * exited.  But if the status is not expected,
  266.              * print a warning message.
  267.              */
  268.             if (status != PROC_INVALID_PID && status != SUCCESS) {
  269.             fprintf(stderr, "Couldn't find migrated pid \"%x\": %s.\n",
  270.                 info.peerProcessID, Stat_GetMsg(status));
  271.             }
  272.             continue;
  273.         } else {
  274.             UpdateMigInfo(&migInfo, &info);
  275.         }
  276.         }
  277.         if (i == (argc-1)) {
  278.         lastPCB = 1;
  279.         }
  280.         (*(printProc[printIndex]))(&info, &argString);
  281.     }
  282.     } else {
  283. #define NUM_PCBS 256
  284.     Proc_PCBInfo infos[NUM_PCBS];
  285.     Proc_PCBInfo migInfo;
  286.     ControlBlock blocks[NUM_PCBS];
  287.     Proc_PCBArgString argStrings[NUM_PCBS];
  288.     int numToPrint, uid;
  289.     register Proc_PCBInfo *infoPtr;
  290.  
  291.     /*
  292.      * Dump the entire process table into our memory.
  293.      */
  294.  
  295.     status = Proc_GetPCBInfo(0, NUM_PCBS-1, PROC_MY_HOSTID,
  296.                  sizeof(Proc_PCBInfo),
  297.                  infos, argStrings, &pcbsUsed);
  298.     if (status != SUCCESS) {
  299.         fprintf(stderr, "Couldn't read process table: %s\n",
  300.             Stat_GetMsg(status));
  301.         exit(1);
  302.     }
  303.  
  304.     /*
  305.      * Collect info into blocks suitable for sorting.  Along the way,
  306.      * filter out irrelevant processes.
  307.      */
  308.  
  309.     uid = geteuid();
  310.     for (i = 0, numToPrint = 0, infoPtr = infos; i < pcbsUsed;
  311.         i++, infoPtr++) {
  312.         if (infoPtr->state == PROC_UNUSED) {
  313.         if (!AFlag) {
  314.             continue;
  315.         }
  316.         goto keepThisProc;
  317.         }
  318.         if (infoPtr->genFlags & PROC_KERNEL) {
  319.         if (!kFlag) {
  320.             continue;
  321.         }
  322.         goto keepThisProc;
  323.         }
  324.         if ((!aFlag) && (uid != infoPtr->effectiveUserID)) {
  325.            continue;
  326.         }
  327.         if (dFlag) {
  328.         if ((infoPtr->state != PROC_SUSPENDED) || !(infoPtr->genFlags
  329.             & (PROC_DEBUGGED | PROC_ON_DEBUG_LIST))) {
  330.             continue;
  331.         }
  332.         }
  333.         if (mFlag &&
  334.         !((infoPtr->genFlags & PROC_FOREIGN)
  335.           || (infoPtr->state == PROC_MIGRATED))) {
  336.         continue;
  337.         }
  338.  
  339.         keepThisProc:
  340.         /*
  341.          * When not printing migration info, follow migrated processes.
  342.          */
  343.         if (printIndex != MIG &&  infoPtr->state == PROC_MIGRATED &&
  344.         !lFlag) {
  345.         status = Proc_GetPCBInfo(Proc_PIDToIndex(infoPtr->peerProcessID),
  346.                      Proc_PIDToIndex(infoPtr->peerProcessID),
  347.                      infoPtr->peerHostID,
  348.                      sizeof(migInfo), &migInfo, 
  349.                      (Proc_PCBArgString *) NULL,
  350.                      (int *) NULL);
  351.         if (status != SUCCESS || migInfo.state == PROC_UNUSED) {
  352.             /*
  353.              * Skip an entry that's migrated locally and doesn't
  354.              * exist remotely -- a race condition between the time
  355.              * we found out about the process and the time the process
  356.              * exited.
  357.              */
  358.             if (status != PROC_INVALID_PID && status != SUCCESS) {
  359.             fprintf(stderr, "Couldn't find migrated pid \"%x\": %s.\n",
  360.                 infoPtr->peerProcessID, Stat_GetMsg(status));
  361.             }
  362.             continue;
  363.         } else {
  364.             UpdateMigInfo(&migInfo, infoPtr);
  365.         }
  366.         }
  367.         blocks[numToPrint].infoPtr = infoPtr;
  368.         blocks[numToPrint].argString = &argStrings[i];
  369.         numToPrint++;
  370.     }
  371.  
  372.     /*
  373.          * Sort the processes, if a sorting procedure has been supplied.
  374.      */
  375.  
  376.     if (sortProc[printIndex] != 0) {
  377.         qsort((char *) blocks, numToPrint, sizeof(ControlBlock),
  378.             sortProc[printIndex]);
  379.     }
  380.  
  381.     /*
  382.      * Print them out in order.
  383.      */
  384.  
  385.     for (i = 0; i < numToPrint; i++) {
  386.         if (i == (numToPrint-1)) {
  387.         lastPCB = 1;
  388.         }
  389.         (*(printProc[printIndex]))(blocks[i].infoPtr, blocks[i].argString);
  390.     }
  391.     }
  392.     exit(0);
  393. }
  394.  
  395. /*
  396.  *----------------------------------------------------------------------
  397.  *
  398.  * UpdateMigInfo --
  399.  *
  400.  *    Update relevant information for a migrated process using the
  401.  *    PCB info obtained from its current host.
  402.  *
  403.  * Results:
  404.  *    None.
  405.  *
  406.  * Side effects:
  407.  *    Fields are copied from one structure to the other.
  408.  *
  409.  *----------------------------------------------------------------------
  410.  */
  411.  
  412. void
  413. UpdateMigInfo(migPtr, infoPtr)
  414.     Proc_PCBInfo *migPtr;    /* Pointer to control block on current host. */
  415.     Proc_PCBInfo *infoPtr;    /* Pointer to control block on this host. */
  416. {
  417.     register int i;
  418.     
  419.     infoPtr->processor = migPtr->processor;
  420.     infoPtr->state = migPtr->state;    
  421.     infoPtr->genFlags = migPtr->genFlags;
  422.     infoPtr->event = migPtr->event;
  423.     infoPtr->billingRate = migPtr->billingRate;
  424.     infoPtr->recentUsage = migPtr->recentUsage;
  425.     infoPtr->weightedUsage = migPtr->weightedUsage;
  426.     infoPtr->unweightedUsage = migPtr->unweightedUsage;
  427.     infoPtr->kernelCpuUsage = migPtr->kernelCpuUsage;
  428.     infoPtr->userCpuUsage = migPtr->userCpuUsage;
  429.     infoPtr->childKernelCpuUsage = migPtr->childKernelCpuUsage;
  430.     infoPtr->childUserCpuUsage = migPtr->childUserCpuUsage;
  431.     infoPtr->numQuantumEnds = migPtr->numQuantumEnds;
  432.     infoPtr->numWaitEvents = migPtr->numWaitEvents;
  433.     infoPtr->schedQuantumTicks = migPtr->schedQuantumTicks;
  434.     for (i = 0; i < VM_NUM_SEGMENTS; i++) {
  435.     infoPtr->vmSegments[i] = migPtr->vmSegments[i];
  436.     }
  437.     infoPtr->sigHoldMask = migPtr->sigHoldMask;
  438.     infoPtr->sigPendingMask = migPtr->sigPendingMask;
  439.     for (i = 0; i < SIG_NUM_SIGNALS; i++) {
  440.     infoPtr->sigActions[i] = migPtr->sigActions[i];
  441.     }
  442. }
  443.  
  444. /*
  445.  *----------------------------------------------------------------------
  446.  *
  447.  * TimeString --
  448.  *
  449.  *    Given a process control block, return a string indicating how
  450.  *    much CPU time the process has used up.
  451.  *
  452.  * Results:
  453.  *    The return value is a statically-allocated string that holds
  454.  *    the time used by the process, in the format min:sec.
  455.  *
  456.  * Side effects:
  457.  *    None.
  458.  *
  459.  *----------------------------------------------------------------------
  460.  */
  461.  
  462. char *
  463. TimeString(infoPtr)
  464.     Proc_PCBInfo *infoPtr;    /* Pointer to control block. */
  465. {
  466.     Time sum;
  467.     static char result[20];
  468.  
  469.     Time_Add(infoPtr->userCpuUsage, infoPtr->kernelCpuUsage, &sum);
  470.     if (sum.microseconds >= 500000) {
  471.     sum.seconds += 1;
  472.     }
  473.     sprintf(result, "%d:%02d", sum.seconds/60, sum.seconds%60);
  474.     return result;
  475. }
  476.  
  477. /*
  478.  *----------------------------------------------------------------------
  479.  *
  480.  * StateString --
  481.  *
  482.  *    Given a process control block, return a string describing
  483.  *    the process's current execution state.
  484.  *
  485.  * Results:
  486.  *    The return value is a statically-allocated string that describes
  487.  *    the process's state.
  488.  *
  489.  * Side effects:
  490.  *    None.
  491.  *
  492.  *----------------------------------------------------------------------
  493.  */
  494.  
  495. char *
  496. StateString(infoPtr)
  497.     Proc_PCBInfo *infoPtr;    /* Pointer to control block. */
  498. {
  499.     switch (infoPtr->state) {
  500.     case PROC_UNUSED:
  501.         return "UNUSD";
  502.         break;
  503.     case PROC_RUNNING:
  504.         return "RUN  ";
  505.         break;
  506.     case PROC_READY:
  507.         return "READY";
  508.         break;
  509.     case PROC_WAITING:
  510.         if (infoPtr->event == -1) {
  511.         return "RWAIT";
  512.         } else {
  513.         return "WAIT ";
  514.         }
  515.         break;
  516.     case PROC_EXITING:
  517.         return "EXIT ";
  518.         break;
  519.     case PROC_DEAD:
  520.         return "DEAD ";
  521.         break;
  522.     case PROC_MIGRATING:
  523.         return "->MIG";
  524.         break;
  525.     case PROC_MIGRATED:
  526.         return "MIG  ";
  527.         break;
  528.     case PROC_NEW:
  529.         return "NEW  ";
  530.         break;
  531.     case PROC_SUSPENDED:
  532.         if (infoPtr->genFlags & (PROC_DEBUGGED | PROC_ON_DEBUG_LIST)) {
  533.         return "DEBUG";
  534.         } else {
  535.         return "SUSP ";
  536.         }
  537.         break;
  538.     }
  539.     return "?Huh?";
  540. }
  541.  
  542. /*
  543.  *----------------------------------------------------------------------
  544.  *
  545.  * PriString --
  546.  *
  547.  *    Given a process's billing rate, return a string describing
  548.  *    the process's priority.
  549.  *
  550.  * Results:
  551.  *    The return value is a statically-allocated string that describes
  552.  *    the process's priority.
  553.  *
  554.  * Side effects:
  555.  *    None.
  556.  *
  557.  *----------------------------------------------------------------------
  558.  */
  559.  
  560. char *
  561. PriString(billingRate)
  562.     int billingRate;    /* process's kernel billing rate. */
  563. {
  564.     /*
  565.      * The default billing rate is 0.  Predefined constants are set up
  566.      * for -2 to 2.  2 is for servers and means they are not billed at all.
  567.      */
  568.     switch (billingRate) {
  569.     case 0:
  570.         return("");
  571.     case -1:
  572.         return("<");
  573.     case 1:
  574.         return(">");
  575.     case 2:
  576.         return("S");
  577.     case -2:
  578.         return("<<");
  579.     default: {
  580.         if (billingRate > 2) {
  581.         return("S");
  582.         }
  583.         return("<<");
  584.     }
  585.  
  586.     }
  587. }
  588.  
  589. /*
  590.  *----------------------------------------------------------------------
  591.  *
  592.  * ArgString --
  593.  *
  594.  *    Given an argument string for a command, modifies the string
  595.  *    to fit the line width for output.
  596.  *
  597.  * Results:
  598.  *    The return value is a pointer to the argument string.
  599.  *
  600.  * Side effects:
  601.  *    The argument string may be shortened by chopping off characters
  602.  *    and adding "...", if it is too long to fit on a single output line.
  603.  *
  604.  *----------------------------------------------------------------------
  605.  */
  606.  
  607. char *
  608. ArgString(argPtr, colsTaken)
  609.     Proc_PCBArgString *argPtr;    /* Pointer to info about command line for
  610.                  * process. */
  611.     int colsTaken;        /* Number of output columns already taken up
  612.                  * by other printed information. */
  613. {
  614.     int charsToKeep;
  615.     char *arg;
  616.     register char *p;
  617.  
  618.     arg = argPtr->argString;
  619.     charsToKeep = lineWidth - colsTaken;
  620.     if (charsToKeep < 10) {
  621.     charsToKeep = 10;
  622.     }
  623.     if (strlen(arg) <= charsToKeep) {
  624.     return arg;
  625.     }
  626.  
  627.     /*
  628.      * Chop fields off the command line until reaching something that fits
  629.      * within the line and leaves enough space for an ellipsis.  If not even
  630.      * the command name fits, then print a partial field.
  631.      */
  632.  
  633.     for (p = arg+charsToKeep-4; p > arg; p--) {
  634.     if (isspace(*p)) {
  635.         break;
  636.     }
  637.     }
  638.     if (p == arg) {
  639.     p = arg+charsToKeep-3;
  640.     } else {
  641.     for (p--; isspace(*p); p--) {
  642.         /* Null loop body;  just back over extra spaces. */
  643.     }
  644.     p += 2;
  645.     }
  646.     p[0] = '.';
  647.     p[1] = '.';
  648.     p[2] = '.';
  649.     p[3] = 0;
  650.     return arg;
  651. }
  652.  
  653. /*
  654.  *----------------------------------------------------------------------
  655.  *
  656.  * UserString --
  657.  *
  658.  *    Given a user id, return a string identifying the user.
  659.  *
  660.  * Results:
  661.  *    The return value is a pointer to a statically-allocated
  662.  *    string identifying the user.
  663.  *
  664.  * Side effects:
  665.  *    User information gets cached in a hash table.
  666.  *
  667.  *----------------------------------------------------------------------
  668.  */
  669.  
  670. char *
  671. UserString(uid)
  672.     int uid;            /* User id to find name for. */
  673. {
  674.     static Hash_Table table;
  675.     static int init = 0;
  676.     char *result;
  677.     register Hash_Entry *entry;
  678.     register struct passwd *passwd;
  679.     int new;
  680.  
  681.     if (!init) {
  682.     init = 1;
  683.     Hash_InitTable(&table, -1, HASH_ONE_WORD_KEYS);
  684.     }
  685.  
  686.     /*
  687.      * See if we've already looked up this process id.
  688.      */
  689.  
  690.     entry = Hash_CreateEntry(&table, (Address) uid, &new);
  691.     if (!new) {
  692.     return (char *) Hash_GetValue(entry);
  693.     }
  694.  
  695.     /*
  696.      * Never heard of this process id before.  Look it up in the
  697.      * password file and fill in the hash table entry.
  698.      */
  699.  
  700.     passwd = getpwuid(uid);
  701.     if (passwd == NULL) {
  702.     result = "???";
  703.     } else {
  704.     result = malloc((unsigned) (strlen(passwd->pw_name) + 1));
  705.     strcpy(result, passwd->pw_name);
  706.     }
  707.     Hash_SetValue(entry, result);
  708.     return result;
  709. }
  710.  
  711. /*
  712.  *----------------------------------------------------------------------
  713.  *
  714.  * HostString --
  715.  *
  716.  *    Given a host id, return a string identifying the host.
  717.  *
  718.  * Results:
  719.  *    The return value is a pointer to a statically-allocated
  720.  *    string identifying the host.
  721.  *
  722.  * Side effects:
  723.  *    Host information gets cached in a hash table.
  724.  *
  725.  *----------------------------------------------------------------------
  726.  */
  727.  
  728. char *
  729. HostString(hostID)
  730.     int hostID;            /* Host id to find name for. */
  731. {
  732.     static Hash_Table table;
  733.     static int init = 0;
  734.     char *result, *name;
  735.     register Hash_Entry *entry;
  736.     register Host_Entry *hostPtr;
  737.     char storage[20];
  738.     int new;
  739.  
  740.     if (!init) {
  741.     init = 1;
  742.     Hash_InitTable(&table, -1, HASH_ONE_WORD_KEYS);
  743.     }
  744.  
  745.     /*
  746.      * See if we've already looked up this host id.
  747.      */
  748.  
  749.     entry = Hash_CreateEntry(&table, (Address) hostID, &new);
  750.     if (!new) {
  751.     return (char *) Hash_GetValue(entry);
  752.     }
  753.  
  754.     /*
  755.      * Never heard of this host before.  Look it up in the
  756.      * host file and fill in the hash table entry.
  757.      */
  758.  
  759.     hostPtr = Host_ByID(hostID);
  760.     if (hostPtr == NULL) {
  761.     name = sprintf(storage, "%d", hostID);
  762.     } else {
  763.     if (hostPtr->aliases[0] != NULL) {
  764.         name = hostPtr->aliases[0];
  765.     } else {
  766.         name = hostPtr->name;
  767.     }
  768.     }
  769.     result = malloc((unsigned) (strlen(name) + 1));
  770.     strcpy(result, name);
  771.     Hash_SetValue(entry, result);
  772.     return result;
  773. }
  774.  
  775. /*
  776.  *----------------------------------------------------------------------
  777.  *
  778.  * PctCpuString --
  779.  *
  780.  *    Given an process table entry, return a string indicating what
  781.  *    fraction of recent CPU time has gone to this process.
  782.  *
  783.  * Results:
  784.  *    The return value is a pointer to a statically-allocated
  785.  *    string in the form "xx.y".  The string will change on the
  786.  *    next call to this procedure.
  787.  *
  788.  * Side effects:
  789.  *    None.
  790.  *
  791.  *----------------------------------------------------------------------
  792.  */
  793.  
  794. char *
  795. PctCpuString(infoPtr)
  796.     Proc_PCBInfo *infoPtr;    /* Pointer to control block. */
  797. {
  798.     static char result[10];
  799.     static int init = 0;
  800.     static double scaleFactor;
  801.     double percent;
  802.  
  803.     /*
  804.      * WARNING:  the following definitions must match the corresponding
  805.      * definitions in the kernel's file "schedule.c".
  806.      */
  807.  
  808. #define FORGET_MULTIPLY 14
  809. #define FORGET_SHIFT   4
  810.  
  811.     if (!init) {
  812.     int denom;
  813.  
  814.     init = 1;
  815.     scaleFactor = timerTicksPerSecond;
  816.     denom = 1<<FORGET_SHIFT;
  817.     scaleFactor *= denom;
  818.     scaleFactor /= denom-FORGET_MULTIPLY;
  819.     }
  820.  
  821.     percent = infoPtr->unweightedUsage;
  822.     percent = percent*100.0/scaleFactor;
  823.     sprintf(result, "%4.1f", percent);
  824.     return result;
  825. }
  826.  
  827. /*
  828.  *----------------------------------------------------------------------
  829.  *
  830.  * PrintShort --
  831.  *
  832.  *    This procedure is called to print out process information
  833.  *    in the "short" format.
  834.  *
  835.  * Results:
  836.  *    None.
  837.  *
  838.  * Side effects:
  839.  *    Prints info on standard output.
  840.  *
  841.  *----------------------------------------------------------------------
  842.  */
  843.  
  844. void
  845. PrintShort(infoPtr, argPtr)
  846.     Proc_PCBInfo *infoPtr;    /* Pointer to control block containing
  847.                  * info to be printed. */
  848.     Proc_PCBArgString *argPtr;    /* Pointer to info about command line for
  849.                  * process. */
  850. {
  851.     static int firstTime = 1;
  852.  
  853.     if (firstTime) {
  854.     firstTime = 0;
  855.     printf("PID   STATE   TIME COMMAND\n");
  856.     }
  857.     printf("%5x %s%7s %s\n", infoPtr->processID, StateString(infoPtr),
  858.         TimeString(infoPtr), ArgString(argPtr, 20));
  859. }
  860.  
  861. /*
  862.  *----------------------------------------------------------------------
  863.  *
  864.  * PrintLong --
  865.  *
  866.  *    This procedure is called to print out process information
  867.  *    in the "long" format (requested with the -u switch).
  868.  *
  869.  * Results:
  870.  *    None.
  871.  *
  872.  * Side effects:
  873.  *    Prints info on standard output.
  874.  *
  875.  *----------------------------------------------------------------------
  876.  */
  877.  
  878. void
  879. PrintLong(infoPtr, argPtr)
  880.     Proc_PCBInfo *infoPtr;    /* Pointer to control block containing
  881.                  * info to be printed. */
  882.     Proc_PCBArgString *argPtr;    /* Pointer to info about command line for
  883.                  * process. */
  884. {
  885.     static int firstTime = 1;
  886.     Vm_Stat vmStat;
  887.     Vm_SegmentInfo segBuf[VM_NUM_SEGMENTS];
  888.     ReturnStatus status;
  889.     int rss, size;
  890.     double pctMem;
  891.  
  892.     if (firstTime) {
  893.     firstTime = 0;
  894.     printf("USER     PID   %%CPU %%MEM  SIZE   RSS STATE   TIME PR COMMAND\n");
  895.     status = Vm_Cmd(VM_GET_STATS, &vmStat);
  896.     if (status != SUCCESS) {
  897.         fprintf(stderr, "Couldn't read Vm statistics: %s\n",
  898.             Stat_GetMsg(status));
  899.         exit(1);
  900.     }
  901.     }
  902.     if (infoPtr->genFlags & PROC_KERNEL) {
  903.     rss = size = 0;
  904.     } else {
  905.     status = Vm_GetSegInfo(infoPtr, 0, sizeof(Vm_SegmentInfo), 
  906.                       &(segBuf[1]));
  907.     switch(status) {
  908.         case SUCCESS:
  909.         rss = segBuf[VM_CODE].resPages + segBuf[VM_HEAP].resPages
  910.             + segBuf[VM_STACK].resPages;
  911.         size = segBuf[VM_CODE].numPages + segBuf[VM_HEAP].numPages
  912.             + segBuf[VM_STACK].numPages;
  913.         break;
  914.         case SYS_INVALID_ARG:
  915.         rss = -1;
  916.         break;
  917.         default: 
  918.         fprintf(stderr, "Couldn't read segment info for pid %x: %s\n",
  919.             infoPtr->processID, Stat_GetMsg(status));
  920.         return;
  921.     }
  922.     }
  923.     if (status == SUCCESS) {
  924.     pctMem = rss;
  925.     rss *= pageSizeInKiloBytes;
  926.     size *= pageSizeInKiloBytes;
  927.     pctMem = (pctMem*100.0)/vmStat.numPhysPages;
  928.     printf("%-8.8s %5x %.8s %4.1f%6d%6d %s%7s %2s %s\n",
  929.         UserString(infoPtr->effectiveUserID),
  930.         infoPtr->processID, PctCpuString(infoPtr), pctMem, size, rss,
  931.         StateString(infoPtr), TimeString(infoPtr),
  932.            PriString(infoPtr->billingRate), ArgString(argPtr, 53));
  933.     } else {
  934.     printf("%-8.8s %5x %.8s %4s%6s%6s %s%7s %2s %s\n",
  935.         UserString(infoPtr->effectiveUserID),
  936.         infoPtr->processID, PctCpuString(infoPtr),"---","---","---",
  937.         StateString(infoPtr), TimeString(infoPtr), 
  938.         PriString(infoPtr->billingRate), ArgString(argPtr, 53));
  939.     }
  940. }
  941.  
  942. /*
  943.  *----------------------------------------------------------------------
  944.  *
  945.  * PrintIDs --
  946.  *
  947.  *    This procedure is called to print out process information
  948.  *    in the form of various ids.
  949.  *
  950.  * Results:
  951.  *    None.
  952.  *
  953.  * Side effects:
  954.  *    Prints info on standard output.
  955.  *
  956.  *----------------------------------------------------------------------
  957.  */
  958.  
  959. void
  960. PrintIDs(infoPtr, argPtr)
  961.     Proc_PCBInfo *infoPtr;    /* Pointer to control block containing
  962.                  * info to be printed. */
  963.     Proc_PCBArgString *argPtr;    /* Pointer to info about command line for
  964.                  * process. */
  965. {
  966.     static int firstTime = 1;
  967.     char storage[10];
  968.     char *family;
  969.  
  970.     if (firstTime) {
  971.     firstTime = 0;
  972.     printf("PID   PPID  GROUP USER     RUSER      TIME COMMAND\n");
  973.     }
  974.     if (infoPtr->familyID == -1) {
  975.     family = "   -1";
  976.     } else {
  977.     family = sprintf(storage, "%5x", infoPtr->familyID);
  978.     }
  979.     printf("%5x %5x %s %-8.8s %-8.8s%7s %s\n",
  980.         infoPtr->processID, infoPtr->parentID, family,
  981.         UserString(infoPtr->effectiveUserID), 
  982.         UserString(infoPtr->userID), TimeString(infoPtr),
  983.         ArgString(argPtr, 43));
  984. }
  985.  
  986. /*
  987.  *----------------------------------------------------------------------
  988.  *
  989.  * PrintVM --
  990.  *
  991.  *    This procedure is called to print out vm-related information
  992.  *    for processes.
  993.  *
  994.  * Results:
  995.  *    None.
  996.  *
  997.  * Side effects:
  998.  *    Prints info on standard output.
  999.  *
  1000.  *----------------------------------------------------------------------
  1001.  */
  1002.  
  1003. void
  1004. PrintVM(infoPtr, argPtr)
  1005.     Proc_PCBInfo *infoPtr;    /* Pointer to control block containing
  1006.                  * info to be printed. */
  1007.     Proc_PCBArgString *argPtr;    /* Pointer to info about command line for
  1008.                  * process. */
  1009. {
  1010.     static int firstTime = 1;
  1011.     static int sizes[VM_NUM_SEGMENTS], rss[VM_NUM_SEGMENTS];
  1012.     static char *names[] = {"system", "code", "heap", "stack"};
  1013.     int totalSize, totalRss, i;
  1014.     Vm_SegmentInfo segBuf[VM_NUM_SEGMENTS];
  1015.     ReturnStatus status;
  1016. #define TOTAL_SEGS 256
  1017.     char segSeen[TOTAL_SEGS];
  1018.  
  1019.     if (firstTime) {
  1020.     firstTime = 0;
  1021.     printf("PID   CODSZ CODRS  HPSZ  HPRS STKSZ STKRS  SIZE   RSS COMMAND\n");
  1022.     }
  1023.  
  1024.     /*
  1025.      * Kernel processes have no memory, so skip them.
  1026.      */
  1027.  
  1028.     if (infoPtr->genFlags & PROC_KERNEL) {
  1029.     return;
  1030.     }
  1031.     status = Vm_GetSegInfo(infoPtr, 0, sizeof(Vm_SegmentInfo), &(segBuf[1]));
  1032.     if (status == SYS_INVALID_ARG) {
  1033.     totalSize = -1;
  1034.     } else if (status != SUCCESS) {
  1035.     fprintf(stderr, "Couldn't read segment info for pid %x: %s\n",
  1036.         infoPtr->processID, Stat_GetMsg(status));
  1037.     return;
  1038.     }
  1039.     if (status == SUCCESS) {
  1040.     totalSize = totalRss = 0;
  1041.     for (i = VM_CODE; i < VM_NUM_SEGMENTS; i++) {
  1042.         totalSize += segBuf[i].numPages*(pageSizeInKiloBytes);
  1043.         totalRss += segBuf[i].resPages*(pageSizeInKiloBytes);
  1044.         if (segBuf[i].segNum >= TOTAL_SEGS) {
  1045.         fprintf(stderr, "Pid %x has %s segment %d:  too large.\n",
  1046.             infoPtr->processID, names[i], segBuf[i].segNum);
  1047.         continue;
  1048.         }
  1049.         if (segSeen[segBuf[i].segNum]) {
  1050.         continue;
  1051.         }
  1052.         segSeen[segBuf[i].segNum] = 1;
  1053.         sizes[i] += segBuf[i].numPages*(pageSizeInKiloBytes);
  1054.         rss[i] += segBuf[i].resPages*(pageSizeInKiloBytes);
  1055.     }
  1056.     printf("%5x%6d%6d%6d%6d%6d%6d%6d%6d %s\n",
  1057.         infoPtr->processID,
  1058.         segBuf[VM_CODE].numPages*(pageSizeInKiloBytes),
  1059.         segBuf[VM_CODE].resPages*(pageSizeInKiloBytes),
  1060.         segBuf[VM_HEAP].numPages*(pageSizeInKiloBytes),
  1061.         segBuf[VM_HEAP].resPages*(pageSizeInKiloBytes),
  1062.         segBuf[VM_STACK].numPages*(pageSizeInKiloBytes),
  1063.         segBuf[VM_STACK].resPages*(pageSizeInKiloBytes),
  1064.         totalSize, totalRss, ArgString(argPtr, 54));
  1065.     } else {
  1066.     printf("%5x%6s%6s%6s%6s%6s%6s%6s%6s %s\n",
  1067.         infoPtr->processID,"---","---","---","---","---","---",
  1068.         "---","---", ArgString(argPtr, 54));
  1069.     }
  1070.     if (lastPCB) {
  1071.     printf("-----------------------------------------------------\n");
  1072.     printf("Total%6d%6d%6d%6d%6d%6d%6d%6d\n",
  1073.         sizes[VM_CODE], rss[VM_CODE], sizes[VM_HEAP], rss[VM_HEAP],
  1074.         sizes[VM_STACK], rss[VM_STACK],
  1075.         sizes[VM_CODE] + sizes[VM_HEAP] + sizes[VM_STACK],
  1076.         rss[VM_CODE] + rss[VM_HEAP] + rss[VM_STACK]);
  1077.     }
  1078. }
  1079.  
  1080. /*
  1081.  *----------------------------------------------------------------------
  1082.  *
  1083.  * PrintMigration --
  1084.  *
  1085.  *    This procedure is called to print out migration-related information
  1086.  *    for processes.
  1087.  *
  1088.  * Results:
  1089.  *    None.
  1090.  *
  1091.  * Side effects:
  1092.  *    Prints info on standard output.
  1093.  *
  1094.  *----------------------------------------------------------------------
  1095.  */
  1096.  
  1097. void
  1098. PrintMigration(infoPtr, argPtr)
  1099.     Proc_PCBInfo *infoPtr;    /* Pointer to control block containing
  1100.                  * info to be printed. */
  1101.     Proc_PCBArgString *argPtr;    /* Pointer to info about command line for
  1102.                  * process. */
  1103. {
  1104.     static int firstTime = 1;
  1105.  
  1106.     if (firstTime) {
  1107.     firstTime = 0;
  1108.     printf("PID   STATE   FLAGS    EVENT RNODE       RPID COMMAND\n");
  1109.     }
  1110.     if ((infoPtr->genFlags & PROC_FOREIGN)
  1111.         || (infoPtr->state == PROC_MIGRATED)) {
  1112.     printf("%5x %s%8x %8x %-10.10s %5x %s\n",
  1113.         infoPtr->processID, StateString(infoPtr),
  1114.         infoPtr->genFlags, infoPtr->event,
  1115.         HostString(infoPtr->peerHostID),
  1116.         infoPtr->peerProcessID,
  1117.         ArgString(argPtr, 46));
  1118.     } else {
  1119.     printf("%5x %s%8x %8x                  %s\n",
  1120.         infoPtr->processID, StateString(infoPtr),
  1121.         infoPtr->genFlags, infoPtr->event,
  1122.         ArgString(argPtr, 46));
  1123.     }
  1124. }
  1125.  
  1126. /*
  1127.  *----------------------------------------------------------------------
  1128.  *
  1129.  * PrintSignals --
  1130.  *
  1131.  *    This procedure is called to print out signal-related information
  1132.  *    for processes.
  1133.  *
  1134.  * Results:
  1135.  *    None.
  1136.  *
  1137.  * Side effects:
  1138.  *    Prints info on standard output.
  1139.  *
  1140.  *----------------------------------------------------------------------
  1141.  */
  1142.  
  1143. void
  1144. PrintSignals(infoPtr, argPtr)
  1145.     Proc_PCBInfo *infoPtr;    /* Pointer to control block containing
  1146.                  * info to be printed. */
  1147.     Proc_PCBArgString *argPtr;    /* Pointer to info about command line for
  1148.                  * process. */
  1149. {
  1150.     static int firstTime = 1;
  1151.     int ignore, handle, i;
  1152.  
  1153.     if (firstTime) {
  1154.     firstTime = 0;
  1155.     printf("PID    PENDING     HELD   IGNORE   HANDLE COMMAND\n");
  1156.     }
  1157.  
  1158.     ignore = handle = 0;
  1159.     for (i = 1; i <= SIG_NUM_SIGNALS; i++) {
  1160.     if (infoPtr->sigActions[i] == SIG_IGNORE_ACTION) {
  1161.         ignore |= 1<<(i-1);
  1162.     } else if (infoPtr->sigActions[i] >> SIG_NUM_ACTIONS) {
  1163.         handle |= 1<<(i-1);
  1164.     }
  1165.     }
  1166.     printf("%5x %8x %8x %8x %8x %s\n", infoPtr->processID,
  1167.         infoPtr->sigPendingMask, infoPtr->sigHoldMask,
  1168.         ignore, handle, ArgString(argPtr, 42));
  1169. }
  1170.  
  1171. /*
  1172.  *----------------------------------------------------------------------
  1173.  *
  1174.  * UsageSort --
  1175.  *
  1176.  *    This procedure is called while sorting the process table
  1177.  *    entries.  It returns a value that will sort the processes
  1178.  *    in decreasing order of recent CPU usage.
  1179.  *
  1180.  * Results:
  1181.  *    Returns < 0 if first's usage is > second's usage, >0 otherwise.
  1182.  *
  1183.  * Side effects:
  1184.  *    None.
  1185.  *
  1186.  *----------------------------------------------------------------------
  1187.  */
  1188.  
  1189. int
  1190. UsageSort(first, second)
  1191.     ControlBlock *first, *second;    /* Two PCBs to compare. */
  1192. {
  1193.     int i;
  1194.     i = second->infoPtr->unweightedUsage - first->infoPtr->unweightedUsage;
  1195.     return i;
  1196. }
  1197.  
  1198. /*
  1199.  *----------------------------------------------------------------------
  1200.  *
  1201.  * AgeSort --
  1202.  *
  1203.  *    This procedure is called while sorting the process table
  1204.  *    entries.  It attempts to return a value that will sort
  1205.  *    processes by age.
  1206.  *
  1207.  * Results:
  1208.  *    Returns < 0 if first is older than second.
  1209.  *
  1210.  * Side effects:
  1211.  *    None.
  1212.  *
  1213.  *----------------------------------------------------------------------
  1214.  */
  1215.  
  1216. int
  1217. AgeSort(first, second)
  1218.     ControlBlock *first, *second;    /* Two PCBs to compare. */
  1219. {
  1220. #ifdef NOTDEF
  1221.     Time    firstTime, secondTime;
  1222.  
  1223.     /*
  1224.      * Unfortunately there's no direct indicator of age in the PCB.
  1225.      * Instead, use the total time used by the process and all its
  1226.      * children as a crude approximation.
  1227.      */
  1228.  
  1229.     Time_Add(first->infoPtr->kernelCpuUsage,
  1230.          first->infoPtr->userCpuUsage, &firstTime);
  1231.     Time_Add(first->infoPtr->childKernelCpuUsage,
  1232.           firstTime, &firstTime);
  1233.     Time_Add(first->infoPtr->childUserCpuUsage,
  1234.         firstTime, &firstTime);
  1235.     Time_Add(second->infoPtr->kernelCpuUsage,
  1236.         second->infoPtr->userCpuUsage, &secondTime);
  1237.     Time_Add(second->infoPtr->childKernelCpuUsage,
  1238.         secondTime, &secondTime);
  1239.     Time_Add(second->infoPtr->childUserCpuUsage,
  1240.         secondTime, &secondTime);
  1241.     if Time_GT(secondTicks, firstTime) {
  1242.     return 1;
  1243.     } else {
  1244.     return -1;
  1245.     }
  1246. #endif
  1247.     return second->infoPtr->numWaitEvents - first->infoPtr->numWaitEvents;
  1248. }
  1249.